﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows;

namespace CSNN
{
    public class NeuronRect
    {
        public Point StartP;
        public Point inP, outP;
        public const int NeuronSize = 30;

        public NeuronRect(int x, int y)
        {
            StartP = new Point(x, y);
            inP = new Point(StartP.X,StartP.Y + NeuronSize / 2);
            outP = new Point(StartP.X + NeuronSize, StartP.Y + NeuronSize / 2);
        }
    }
    
    /// <summary>
    /// Отображение нейронной сети
    /// </summary>
    public class NeuralNetShow
    {
        #region Члены класса
        
        /// <summary>
        /// Отступ от границ полотна
        /// </summary>
        private const byte Indentation = 15;

        /// <summary>
        /// Место для отображения входов/выходов
        /// </summary>
        private const byte SpaceForInputsOutputs = 200;

        /// <summary>
        /// Ширина прямоугольника, отображающего нейрон
        /// </summary>
        public const byte NeuronSize = 30;

        /// <summary>
        /// Отступ вокруг нейрона
        /// </summary>
        public const byte SpaceAroundNeuron = 5;

        /// <summary>
        /// Промежуток между слоями нейронной сети
        /// </summary>
        public const byte SpaceBetweenLayers = 20;

        /// <summary>
        /// Центр полотна
        /// </summary>
        private int midX, midY;

        /// <summary>
        /// Высота и ширина полотна
        /// </summary>
        private int CanvasHeight, CanvasWidth;

        /// <summary>
        /// Экземпляр нейронной сети
        /// </summary>
        private NeuralNetwork NetForShow;

        /// <summary>
        /// "Полотно" для отображения
        /// </summary>
        private System.Windows.Controls.Image Canvas;

        /// <summary>
        /// Рисовалка
        /// </summary>
        private DrawingVisual drawPlace;

        /// <summary>
        /// Кординаты нейронов на полотне
        /// </summary>
        private NeuronRect[][] NeuronsRects;

        /// <summary>
        /// Точки входов
        /// </summary>
        private Point[] Inputs;

        /// <summary>
        /// Точки выходов
        /// </summary>
        private Point[] Outputs;

        #endregion

        public NeuralNetShow(NeuralNetwork Net, System.Windows.Controls.Image Canvas)
        {
            this.NetForShow = Net;
            this.Canvas = Canvas;

            Inputs = new Point[Net.NeuralLayers[0].NeuronsDemension];
            Outputs = new Point[Net.NeuralLayers[Net.LayersCount-1].NeuronsCount];

            this.NeuronsRects = new NeuronRect[NetForShow.NeuronsInNet][];

            int max = Net.NeuralLayers[0].NeuronsDemension * 12 + 30;
            int maxNeuronsCount = Net.NeuralLayers[0].NeuronsCount;
            for (int i = 0; i < Net.LayersCount; i++)
            {
                NeuronsRects[i] = new NeuronRect[Net.NeuralLayers[i].NeuronsCount];                
                if (maxNeuronsCount < Net.NeuralLayers[i].NeuronsCount)
                    maxNeuronsCount = Net.NeuralLayers[i].NeuronsCount;
            }
            int pixNeuro = maxNeuronsCount * (NeuronSize + 2 * SpaceAroundNeuron) + 2 * Indentation;
            if (max < pixNeuro) max = pixNeuro;

            CanvasHeight = max;
            CanvasWidth = Net.LayersCount * (NeuronSize + 2 * SpaceAroundNeuron + SpaceBetweenLayers) + 2 * Indentation + SpaceForInputsOutputs;

            Canvas.Height = CanvasHeight;
            Canvas.Width = CanvasWidth;

            midX = CanvasWidth / 2;
            midY = CanvasHeight / 2;

            this.drawPlace = new DrawingVisual();
        }

        /// <summary>
        /// Прорисовка и калькуляция начальных значений полотна
        /// </summary>
        public void Draw()
        {
            int xDraw = 100, yDraw = 0, yInputDraw = 0; // переменные отступа
            DrawingContext dc = drawPlace.RenderOpen();
            Pen penForInputs = new Pen(Brushes.Black, 1);
            Pen penForDraw = new Pen(Brushes.Black, 1);
            Pen penForNeuron = new Pen(Brushes.Black, 2);

            for (int i = 0; i < NetForShow.LayersCount; i++)
            {
                int LayerHeigth = NetForShow.NeuralLayers[i].NeuronsCount * (NeuronSize + 2 * SpaceAroundNeuron);

                yDraw = midY - (LayerHeigth / 2) + Indentation;

                for (int j = 0; j < NetForShow.NeuralLayers[i].NeuronsCount; j++)
                {// прорисовка нейронов
                    NeuronsRects[i][j] = new NeuronRect
                        (i * (NeuronSize + 2 * SpaceAroundNeuron + SpaceBetweenLayers) + SpaceAroundNeuron + Indentation + xDraw, 
                        j * (NeuronSize + 2 * SpaceAroundNeuron) + SpaceAroundNeuron + yDraw);

                    dc.DrawRectangle(Brushes.White, penForNeuron, 
                        new System.Windows.Rect(
                            NeuronsRects[i][j].StartP.X,
                            NeuronsRects[i][j].StartP.Y,
                            NeuronSize,
                            NeuronSize
                            ));                       
                }

                if (i == 0)
                {// прорисовка связей входов и нейронной сети
                    
                    yInputDraw = midY - (NetForShow.NeuralLayers[0].NeuronsDemension * 12 / 2) + Indentation;
                    for (int c = 0; c < NetForShow.NeuralLayers[i].NeuronsDemension; c++)
                    {
                        Inputs[c] = new Point(2 * Indentation + 10, c * 12 + Indentation + yInputDraw);
                    }
                    for (int j = 0; j < NetForShow.NeuralLayers[i].NeuronsCount; j++)
                    {
                        for (int c = 0; c < NetForShow.NeuralLayers[i].NeuronsDemension; c++)
                        {
                            dc.DrawLine(penForInputs, Inputs[c], NeuronsRects[i][j].inP);
                        }
                    }
                    for (int c = 0; c < NetForShow.NeuralLayers[i].NeuronsDemension; c++)
                    {
                        dc.DrawEllipse(Brushes.White, penForDraw, Inputs[c], 4, 4);
                    }
                }
                if (i > 0)
                {// прорисовка промежуточных синапсов
                    for (int j = 0; j < NetForShow.NeuralLayers[i].NeuronsCount; j++)
                    {
                        for (int c = 0; c < NetForShow.NeuralLayers[i - 1].NeuronsCount; c++)
                        {
                            dc.DrawLine(penForDraw, NeuronsRects[i - 1][c].outP, NeuronsRects[i][j].inP);
                        }
                    }
                }
                if (i == NetForShow.LayersCount - 1)
                {
                    for (int j = 0; j < NetForShow.NeuralLayers[NetForShow.LayersCount - 1].NeuronsCount; j++)
                    {
                        Point p = new Point(NeuronsRects[i][j].outP.X + 30, NeuronsRects[i][j].outP.Y);
                        dc.DrawLine(penForDraw, NeuronsRects[i][j].outP, p);
                        dc.DrawEllipse(Brushes.Black, penForDraw, p, 7, 7);
                    }
                }

            }
            dc.Close();
            Canvas.Source = new DrawingImage(drawPlace.Drawing);
        }

    }
}
